package com.examlpe.lotterysystem.service.Impl;

import com.examlpe.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.examlpe.lotterysystem.common.exception.ServiceException;
import com.examlpe.lotterysystem.common.utils.JacksonUtil;
import com.examlpe.lotterysystem.common.utils.RedisUtil;
import com.examlpe.lotterysystem.controller.param.CreateActivityParam;
import com.examlpe.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.examlpe.lotterysystem.controller.param.CreateUserByActivityParam;
import com.examlpe.lotterysystem.controller.param.PageParam;
import com.examlpe.lotterysystem.dao.dataobject.ActivityDO;
import com.examlpe.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.examlpe.lotterysystem.dao.dataobject.ActivityUserDO;
import com.examlpe.lotterysystem.dao.dataobject.PrizeDO;
import com.examlpe.lotterysystem.dao.mapper.*;
import com.examlpe.lotterysystem.service.ActivityService;
import com.examlpe.lotterysystem.service.dto.ActivityDTO;
import com.examlpe.lotterysystem.service.dto.ActivityDetailDTO;
import com.examlpe.lotterysystem.service.dto.CreatetivityDTO;
import com.examlpe.lotterysystem.service.dto.PageListDTO;
import com.examlpe.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.examlpe.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.examlpe.lotterysystem.service.enums.ActivityStatusEnum;
import com.examlpe.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {
    /*活动缓存前置*/
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /*活动缓存过期时间*/
    private final Long ACTIVITY_TIMEOUT = 60*60*24*3l;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Transactional(rollbackFor = Exception.class)//涉及多表
    @Override
    public CreatetivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);

        //保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());

        activityPrizeMapper.bachInsert(activityPrizeList);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return  activityUserDO;
                }).collect(Collectors.toList());

        activityUserMapper.bachInsert(activityUserDOList);

        //整合完整的活动信息，放入redis里
        //activityId : ActivityDetail: 活动 + 奖品 + 人员

        //先获取奖品基本属性列表
        //获取需要查询的奖品id
        List<Long> prizeIds =  param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam :: getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        System.out.println(prizeIds);
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
       ActivityDetailDTO activityDetailDTO = converToActivityDetailDTO(activityDO,activityUserDOList,
                prizeDOList,activityPrizeList);

       cacheActivity(activityDetailDTO);
        //构造返回值
        CreatetivityDTO createtivityDTO = new CreatetivityDTO();
        createtivityDTO.setActivityId(activityDO.getId());
        return createtivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total = activityMapper.count();
        //查询当前页面列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    activityDTO.setDescription(activityDO.getDescription());
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total,activityDTOList);
    }
    //获取活动完整信息
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId) {
            log.warn("完整活动id为空!");
            return null;
        }
        //从redis中查找完整活动信息
        ActivityDetailDTO activityDetailDTO =getActivityfromCache(activityId);
        if(null != activityDetailDTO) {
            log.info("查询活动详细信息成功!");
            return activityDetailDTO;
        }
        //Redis不存在从数据库中获取完整活动信息

        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);

        //活动奖品表
        List<ActivityPrizeDO> aPDOList = activityPrizeMapper.selectPrizeByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> aUDOList = activityUserMapper.selectUserByActivityId(activityId);

        //奖品表: 先获取要查询的奖品id
        List<Long> prizeIds = aPDOList.stream().map(ActivityPrizeDO :: getPrizeId).collect(Collectors.toList());
        List<PrizeDO> prizeList = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动信息，存放redis
        activityDetailDTO = converToActivityDetailDTO(aDO,aUDOList,prizeList,aPDOList);
        cacheActivity(activityDetailDTO);
        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId) {
            log.warn("要缓存的id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);
        if(null == aDO) {
            log.error("要缓存的id有误!");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_ERROR);
        }
        //活动奖品表
        List<ActivityPrizeDO> aPDOList = activityPrizeMapper.selectPrizeByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> aUDOList = activityUserMapper.selectUserByActivityId(activityId);

        //奖品表: 先获取要查询的奖品id
        List<Long> prizeIds = aPDOList.stream().map(ActivityPrizeDO :: getPrizeId).collect(Collectors.toList());
        List<PrizeDO> prizeList = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动信息，存放redis
        cacheActivity( converToActivityDetailDTO(aDO,aUDOList,prizeList,aPDOList));
    }

    //缓存完整的活动信息
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //key:AVTIVITY_activityId
        //value:ActivityDerailDTO(json)
        if(null == detailDTO || null == detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在!");
            return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            log.error("缓存活动异常。ActivityDetailDTO={}",JacksonUtil.writeValueAsString(detailDTO),e);
        }

    }
    //从缓存中拿到对应的活动信息
    private ActivityDetailDTO getActivityfromCache(Long activityId) {
        if(null == activityId) {
            log.warn("获取活动数据的activityId为空");
            return null;
        }
        String str =  redisUtil.get(ACTIVITY_PREFIX+activityId);
        if(!StringUtils.hasText(str)) {
            log.warn("获取的缓存数据为空!key{}",ACTIVITY_PREFIX+activityId);
            return null;
        }
        try{
            ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(str,ActivityDetailDTO.class);
            return activityDetailDTO;
        }catch (Exception e) {
           log.error("从缓存中获取活动信息异常。key={}",ACTIVITY_PREFIX+activityId);
           return null;
        }
    }

    private ActivityDetailDTO converToActivityDetailDTO(ActivityDO activityDO,
                                           List<ActivityUserDO> activityUserDOList,
                                           List<PrizeDO> prizeDOList,
                                           List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        //aPDO: {prizeId,amount,status}
        //prizeDO: {prizedId,name...}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(aPDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(aPDO.getPrizeId());

                    Optional<PrizeDO> optionPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(aPDO.getPrizeId()))
                            .findFirst();
                    //如果prizeDO为空，不会传给参数
                    optionPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                    });

                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(aPDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(aPDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(aPDO.getStatus()));


                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);


        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList
                .stream()
                .map(aUDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(aUDO.getUserId());
                    userDTO.setUserName(aUDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(aUDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);

        return activityDetailDTO;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //人员id在人员列表中存在
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct() //去重
                .collect(Collectors.toList());
        List<Long> existUserId = userMapper.selectExistByIds(userIds);
        if(CollectionUtils.isEmpty(existUserId)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if(!existUserId.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //奖品id在奖品表中存在

        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> extistPrizeId = prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(prizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if(!extistPrizeId.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if(userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //校验奖品等级有效性
        param.getActivityPrizeList().forEach(prize -> {
            if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }


}
